package com.wtwd.device.controller;


import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.xiaoymin.knife4j.annotations.DynamicParameter;
import com.github.xiaoymin.knife4j.annotations.DynamicParameters;
import com.wtwd.device.constant.*;
import com.wtwd.device.model.entity.AppUser;
import com.wtwd.device.model.entity.AppUserInfo;
import com.wtwd.device.model.vo.LoginVO;
import com.wtwd.device.model.vo.ResultVO;
import com.wtwd.device.service.AppUserInfoService;
import com.wtwd.device.service.AppUserService;
import com.wtwd.device.service.RedisService;
import com.wtwd.device.service.ThirdPartyUserService;
import com.wtwd.device.util.RSAUtil;
import com.wtwd.device.util.StringUtil;
import com.wtwd.device.util.ThirdPartyUserFactory;
import com.wtwd.device.util.Tools;
import io.swagger.annotations.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author zdl
 * @since 2021-08-04
 */
@Api(tags = "用户管理")
@RestController
@RequestMapping("/app-user")
public class AppUserController {

    @Resource
    private RedisService redisService;

    @Resource
    private AppUserService appUserService;

    @Resource
    private AppUserInfoService appUserInfoService;

    @Resource
    private ThirdPartyUserFactory thirdPartyUserFactory;


    /**
     * 1.校验验证码
     * 2.保存效验账号唯一性
     *
     * @param phoneRegisterModel
     * @return
     */
    @ApiOperation("手机号用户注册")
    @DynamicParameters(name = "phoneRegisterModel", properties = {
            @DynamicParameter(name = "phone", value = "手机号", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "code", value = "验证码", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "area", value = "验证码", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "areaCode", value = "手机区号", dataTypeClass = String.class, required = true, example = "86,不包含区号")
            , @DynamicParameter(name = "password", value = "登录密码", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "appId", value = "AppId,详见文档说明", dataTypeClass = Long.class, required = false)
    })
    @ApiResponses({@ApiResponse(code = 400, message = "参数错误")
            , @ApiResponse(code = 1001, message = "验证码错误")
    })
    @PostMapping("/registerByPhone")
    public ResultVO registerByPhone(@RequestBody JSONObject phoneRegisterModel) {
        String phone = phoneRegisterModel.getString("phone");
        String code = phoneRegisterModel.getString("code");
        String area = phoneRegisterModel.getString("area");
        String areaCode = phoneRegisterModel.getString("areaCode");
        String password = phoneRegisterModel.getString("password");
        if (StringUtils.isAnyBlank(phone, code, area, areaCode, password)) {
            return ResultVO.error(RespCode.PARAMS_ERROR);
        }
        if (StringUtil.isNotNumber(phone)) {
            return ResultVO.error(RespCode.PHONE_FORMAT_ERROR);
        }
        if (!redisService.verifyCode(phone, AuthCodeTypeEnum.REGISTER.getType(), code)) {
            return ResultVO.error(RespCode.AUTH_CODE_ERROR);
        }
        AppUser appUser = new AppUser();
        appUser.setPhone(phone);
        appUser.setAreaCode(areaCode);
        appUser.setArea(area);
        appUser.setPassword(password);
        try {
            if (!appUserService.save(appUser)) {
                return ResultVO.error(RespCode.FAIL);
            }
        } catch (DuplicateKeyException e) {
            return ResultVO.error(RespCode.PHONE_EXIST);
        }
        //保存用户信息
        appUserInfoService.save(appUserService.getDefaultObj(appUser.getUserId()));
        return ResultVO.success();
    }



    /**
     * @param emailRegisterModel
     * @return
     */
    @ApiOperation("邮箱用户注册")
    @DynamicParameters(name = "emailRegisterModel", properties = {
            @DynamicParameter(name = "email", value = "邮箱", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "code", value = "验证码", dataTypeClass = String.class, required = false)
            , @DynamicParameter(name = "password", value = "登录密码", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "appId", value = "AppId,详见文档说明", dataTypeClass = Long.class, required = false)
    })
    @ApiResponses({@ApiResponse(code = 400, message = "参数错误")
            , @ApiResponse(code = 1001, message = "验证码错误")
            , @ApiResponse(code = 1009, message = "邮箱格式有误")
    })
    @PostMapping("/registerByEmail")
    public ResultVO registerByEmail(@RequestBody JSONObject emailRegisterModel) {
        String email = emailRegisterModel.getString("email");
        String password = emailRegisterModel.getString("password");
        String code = emailRegisterModel.getString("code");
        if (StringUtils.isAnyBlank(email, password, code)) {
            return ResultVO.error(RespCode.PARAMS_ERROR);
        }
        if (StringUtil.isNotEmail(email)) {
            return ResultVO.error(RespCode.EMAIL_FORMAT_ERROR);
        }
        if (!redisService.verifyCode(email, AuthCodeTypeEnum.REGISTER.getType(), code)) {
            return ResultVO.error(RespCode.AUTH_CODE_ERROR);
        }
        AppUser appUser = new AppUser();
        appUser.setEmail(email);
        appUser.setPassword(password);
        try {
            if (!appUserService.save(appUser)) {
                return ResultVO.error(RespCode.FAIL);
            }
        } catch (DuplicateKeyException e) {
            return ResultVO.error(RespCode.EMAIL_EXIST);
        }
        //保存用户信息
        appUserInfoService.save(appUserService.getDefaultObj(appUser.getUserId()));
        return ResultVO.success();
    }

    /**
     * 1.验证账号
     * 2.检查是否异地登陆,是发送通知
     * 3.保存登录凭证。
     * 用户id -token
     * 推送key（userId-clientId）
     *
     * @param loginModel
     * @return
     */
    @ApiOperation(value = "账号密码登录", notes = "登录成功后,拿到token,之后所有认证请求需要在请求Head access-token属性中携带 ")
    @DynamicParameters(name = "loginModel", properties = {
            @DynamicParameter(name = "account", value = "账号", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "password", value = "登录密码", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "clientType", value = "客户端类型,1-ios,2-android", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "clientId", value = "客户端唯一标记", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "appId", value = "AppId,详见文档说明", dataTypeClass = Long.class, required = false)
    })
    @ApiResponses({@ApiResponse(code = 400, message = "参数错误")
            , @ApiResponse(code = 1006, message = "账号或密码错误")
    })
    @Transactional
    @PostMapping("/login")
    public ResultVO<LoginVO> login(@RequestBody JSONObject loginModel) {
        String account = loginModel.getString("account");
        String password = loginModel.getString("password");
        String clientType = loginModel.getString("clientType");
        String clientId = loginModel.getString("clientId");
        if (StringUtils.isAnyBlank(account, password)) {
            return ResultVO.error(RespCode.PARAMS_ERROR);
        }
        AppUser user = appUserService.getUserByAccountAndPwd(account, password);
        if (user == null) {
            return ResultVO.error(RespCode.ACCOUNT_PASSWORD_ERROR);
        }
        Long userId = user.getUserId();
        String pushKey = appUserInfoService.getAppUserPushKey(userId);
        String newPushKey = userId + SeparatorConstant.DOT + clientId;
        if (!(newPushKey).equals(pushKey) && StringUtils.isNotBlank(pushKey)) {
            //异地登陆推送消息
            appUserInfoService.pushMsgToAppUser(userId, clientType, AppMsgEnum.EXCP_LOGIN);
        }
        AppUserInfo userInfo = new AppUserInfo();
        userInfo.setUserId(userId);
        userInfo.setClientType(clientType);
        userInfo.setClientId(clientId);
        appUserInfoService.update(userInfo);
        LoginVO loginVO = redisService.createLoginToken(user.getUserId());
        loginVO.setUserId(user.getUserId());
        loginVO.setPushKey(newPushKey);
        return ResultVO.success(loginVO);
    }


    @ApiOperation(value = "游客登录", notes = "登录成功后,拿到token,之后所有认证请求需要在请求Head access-token属性中携带 ")
    @DynamicParameters(name = "loginModel", properties = {
            @DynamicParameter(name = "appDeviceId", value = "用户设备ID", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "appId", value = "AppId,详见文档说明", dataTypeClass = Long.class, required = false)
    })
    @ApiResponses({@ApiResponse(code = 400, message = "参数错误")
    })
    @PostMapping("/touristLogin")
    public ResultVO<LoginVO> loginByTourist(@RequestBody JSONObject loginModel) {
        String appDeviceId = loginModel.getString("appDeviceId");
        if (StringUtils.isAnyBlank(appDeviceId)) {
            return ResultVO.error(RespCode.PARAMS_ERROR);
        }
        AppUser user = appUserService.getUserByAppDeviceId(appDeviceId);
        boolean isFirst = false;
        if (user == null) {//第一次进入
            user = new AppUser();
            user.setAppDeviceId(appDeviceId);
            if (!appUserService.save(user)) {
                return ResultVO.error(RespCode.FAIL);
            }
            isFirst = true;
        }
        LoginVO loginVO = redisService.createLoginToken(user.getUserId());
        loginVO.setUserId(user.getUserId());
        loginVO.setIsFirst(isFirst);
        return ResultVO.success(loginVO);
    }

    @ApiOperation(value = "发送验证码", notes = "captcha,通过获取验证接口的数据解密后通过MD5加密")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "account", value = "账号", required = true, dataTypeClass = String.class)
            , @ApiImplicitParam(name = "accountType", value = "账号类型;1-手机号,2-邮箱,", required = true, dataType = "int")
            , @ApiImplicitParam(name = "areaCode", value = "手机区号;accountType=1并且authType=1 必填,", dataTypeClass = Integer.class)
            , @ApiImplicitParam(name = "authType", value = "验证类型;1-注册,2-忘记密码,3-修改账号", dataType = "int")
            , @ApiImplicitParam(name = "captcha", value = "验证码", dataType = "string")
            , @ApiImplicitParam(name = "appId", value = "AppId,详见文档说明", dataTypeClass = Long.class, required = false)

    })
    @ApiResponses({
            @ApiResponse(code = 400, message = "参数错误")
            , @ApiResponse(code = 1003, message = "手机号已存在")
            , @ApiResponse(code = 1004, message = "邮箱已存在")
            , @ApiResponse(code = 1005, message = "手机号不存在")
            , @ApiResponse(code = 1009, message = "邮箱不存在")
            , @ApiResponse(code = 1011, message = "短信发送错误")
            , @ApiResponse(code = 1012, message = "不支持该手机号")
            , @ApiResponse(code = 1013, message = "该手机号发送频次过高")
            , @ApiResponse(code = 1014, message = "发送频次过高已限额,请明日再试")
    })
    @GetMapping("/authCode")
    public ResultVO getAuthCode(Long appId, String account, String areaCode, Integer accountType, Integer authType, String captcha) {
        if (StringUtils.isAnyBlank(account, captcha) || accountType == null || authType == null) {
            return ResultVO.error(RespCode.PARAMS_ERROR);
        }
        if (!captcha.equals(redisService.getValue(account))) {
            return ResultVO.error(RespCode.FAIL);
        }
        if (UserTypeEnum.PHONE.getType() == accountType) {
            if (StringUtils.isBlank(areaCode)) return ResultVO.error(RespCode.PARAMS_ERROR);
            ResultVO resultVO = appUserService.sendSMSAuthCode(areaCode, account, authType);
            if (resultVO.getCode() == RespCode.SUCCESS.getCode()) {
                boolean isSuccess = redisService.createCode(account, authType, (String) resultVO.getData(), Constant.AUTHCODE_EXPIRE_TIME);
                if (!isSuccess) {
                    return ResultVO.error();
                }
                resultVO.setData(null);
            }
            return resultVO;
        } else if (UserTypeEnum.EMAIL.getType() == accountType) {
            boolean isExistAccount = appUserService.getUserByEmail(account) != null;
            switch (AuthCodeTypeEnum.getAuthCodeTypeEnum(authType)) {
                case MODIFY_PASSWORD:
                    if (!isExistAccount) return ResultVO.error(RespCode.EMAIL_NOT_EXIST);
                    break;
                default:
                    if (isExistAccount) return ResultVO.error(RespCode.EMAIL_EXIST);
            }
            ResultVO resultVO = appUserService.sendEmailAuthCode(account, authType);
            if (resultVO.getData() != null) {
                boolean isSuccess = redisService.createCode(account, authType, (String) resultVO.getData(), Constant.AUTHCODE_EXPIRE_TIME);
                if (!isSuccess) {
                    return ResultVO.error();
                }
            }
            resultVO.setData(null);
            return resultVO;
        }
        return ResultVO.success();
    }

    @ApiOperation(value = "获取验证", notes = "此接口在【发送验证码】之前调用")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "account", value = "账号", required = true, dataTypeClass = String.class)
            , @ApiImplicitParam(name = "accountType", value = "账号类型;1-手机号,2-邮箱,", required = true, dataType = "int")
            , @ApiImplicitParam(name = "areaCode", value = "手机区号;accountType=1并且authType=1 必填,", dataTypeClass = Integer.class)
            , @ApiImplicitParam(name = "authType", value = "验证类型;1-注册,2-忘记密码,3-修改账号", dataType = "int")
            , @ApiImplicitParam(name = "appId", value = "AppId,详见文档说明", dataTypeClass = Long.class, required = false)
    })
    @GetMapping("/captcha")
    public ResultVO<String> getCaptcha(Long appId, String account, String areaCode, Integer accountType, Integer authType) throws Exception {
        String ramdom6 = Tools.getRamdom6();
        redisService.setKey(account, DigestUtil.md5Hex(ramdom6), Constant.AUTHCODE_EXPIRE_TIME);
        return ResultVO.success(RSAUtil.encryptByPrivateKey(ramdom6));
    }

    @ApiOperation(value = "忘记密码", notes = "登录成功后,拿到token,之后所有认证请求需要在请求Head access-token属性中携带")
    @DynamicParameters(name = "loginModel", properties = {
            @DynamicParameter(name = "account", value = "账号", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "areaCode", value = "手机区号;当accountType=1必填", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "authCode", value = "验证码", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "accountType", value = "账号类型;1-手机2-邮箱", dataTypeClass = Integer.class, required = true)
            , @DynamicParameter(name = "appId", value = "AppId,详见文档说明", dataTypeClass = Long.class, required = false)

    })
    @ApiResponses({@ApiResponse(code = 400, message = "参数错误")
            , @ApiResponse(code = 1001, message = "验证码错误")

    })
    @PostMapping("/forgetPassword")
    public ResultVO<LoginVO> forgetPassword(@RequestBody JSONObject loginModel) {
        String account = loginModel.getString("account");
        String authCode = loginModel.getString("authCode");
        Integer accountType = loginModel.getInteger("accountType");
        if (StringUtils.isAnyBlank(account, authCode)) {
            return ResultVO.error(RespCode.PARAMS_ERROR);
        }
        AppUser user = null;
        if (accountType == 1) {
            user = appUserService.getUserByPhone(account);
        } else if (accountType == 2) {
            user = appUserService.getUserByEmail(account);
        }
        if (user == null) {
            return ResultVO.error(RespCode.ACCOUNT_NOT_EXIST);
        }
        if (!redisService.verifyCode(account, AuthCodeTypeEnum.MODIFY_PASSWORD.getType(), authCode)) {
            return ResultVO.error(RespCode.AUTH_CODE_ERROR);
        }
        LoginVO loginVO = redisService.createLoginToken(user.getUserId());
        loginVO.setUserId(user.getUserId());
        return ResultVO.success(loginVO);
    }

    @ApiOperation(value = "删除账号(测试)")
    @DeleteMapping("/account")
    public ResultVO deleteAccount(@RequestParam String account) {
        Integer accountType = 2;
        if (StringUtil.isNotEmail(account)) {
            accountType = 1;
        }
        appUserService.deleteByAccount(account, accountType);
        return ResultVO.success();
    }

    /**
     * 通过code ,获取访问令牌和ID令牌
     *
     * @param code
     * @return
     */
    @GetMapping("/google-login")
    public ResultVO googleLogin(String code) {
        ThirdPartyUserService thirdPartyService = thirdPartyUserFactory.getThirdPartyService(UserSourceEnum.GOOGLE);
        LoginVO login = thirdPartyService.loginTemplate(code);
        return login == null ? ResultVO.error() : ResultVO.success(login);
    }

    @ApiOperation(value = "第三方登陆通过IdToken", notes = "登录成功后,拿到token,之后所有认证请求需要在请求Head access-token属性中携带")
    @DynamicParameters(name = "loginByIdTokenModel", properties = {
            @DynamicParameter(name = "idToken", value = "第三方平台idToken", dataTypeClass = String.class, required = true)
            , @DynamicParameter(name = "userSource", value = "用户来源,google-51;apple-52", dataTypeClass = String.class, required = true)
    })
    @ApiResponses({@ApiResponse(code = 400, message = "参数错误")

    })
    @PostMapping("/loginByIdToken")
    public ResultVO loginByIdToken(@RequestBody JSONObject loginModel) {
        String idToken = loginModel.getString("idToken");
        String userSource = loginModel.getString("userSource");
        ThirdPartyUserService thirdPartyService = thirdPartyUserFactory.getThirdPartyService(UserSourceEnum.getUserSource(userSource));
        LoginVO login = thirdPartyService.loginForIdTokenTemplate(idToken);
        return login == null ? ResultVO.error() : ResultVO.success(login);
    }


    @GetMapping("/auth-google")
    public void googleLogin(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String url = "https://accounts.google.com/o/oauth2/v2/auth?client_id=989741510409-31fscb4vkdq6oeie96890qte7ao72r1k.apps.googleusercontent.com&redirect_uri=http://localhost:9001/app/app-user/google-login&response_type=code&scope=openid email profile";
        response.sendRedirect(url);
    }

}

